/**
 * Created by chenwen on 17-5-15.
 */
let randomString = require("randomstring"),
    xmlbuilder = require("xmlbuilder"),
    parseXml = require("xml2js").parseString,
    Util = require("./util"),
    url = require("url"),
    https = require("https"),
    crypto = require("crypto");



/**
 * 微信类
 */
class Weixin{

    /**
     * 构造器
     * @param appKey
     * @param appid
     * @param mch_id
     * @param sign_type
     */
    constructor(appKey, appid, mch_id,sign_type="md5") {
        this.appKey = appKey;
        this.appid = appid;
        this.mch_id = mch_id;
        this.sign_type = sign_type;
    }

    /**
     * 下订单
     * @data
     *     device_info 设备号
     *     notify_url 通知地址
     *     trade_type 交易类型
     *     body 商品描述
     *     out_trade_no 商户订单号
     *     total_fee 标价金额
     *     spbill_create_ip 终端IP
     * @return promise
     */
    fetchPlaceOrder({device_info="WEB",trade_type="WAP", notify_url, body, out_trade_no, total_fee, spbill_create_ip}) {
        let data = {device_info, notify_url,body, trade_type, out_trade_no, total_fee, spbill_create_ip};

        //生成随机字符串
        data.nonce_str = randomString.generate(32);
        let placeOrder = this.buildWeiChatPlaceOrder(data),
            postData = WeixinUtil.convertObjToXml(placeOrder)
                                    .toString(),
            httpsOptions = WeixinUtil.getWeichatInteractiveHttpOptions(Weixin.placeOrderUrl);
        return this.requestPost(httpsOptions, postData);
    }

    /**
     * 验证订单
     * @param data 微信异步通知的数据
     */
     validateOrder(data){
        let  self = this;
        function validate(data){
            let obj = WeixinUtil.formatResponseData(data.xml);
            let str = WeixinUtil.buildSortString(obj, ["sign"]) +  "&key=" + self.appKey;
            let sign = self.generateSign(str);
            obj.isValid = sign === obj.sign ? true : false;
            return obj;
        }
        function promiseParseXml(data){
            return new Promise(function(reslove, reject){
                parseXml(data, (err, data) => err? reject(err): reslove(data));
            })
        }
        async function gen(data){
            let a = await promiseParseXml(data);
            let obj = await validate(a);
            return obj;
        }

        return new Promise(function(reslove, reject){
            gen(data).then((obj)=> reslove(obj), (err) => reject(err));
        })
    }

    /**
     * 向微信服务器发起请求
     * @param httpsOptions 请求参数
     * @return promise
     */
    requestPost(httpsOptions, postData){
        return new Promise(function(reslove, reject){
            var req = https.request(httpsOptions, function(res) {
                var result = "";
                res.on("data", (chunk) => result+=chunk);

                //解析数据返回
                res.on("end", () => parseXml(result,(error,obj)=> error ? reject(error):reslove(WeixinUtil.formatResponseData(obj.xml))));

            });
            req.on("error", (err)=>reject(err));
            req.write(postData);
            req.end();
        });
    }


    /**
     * 属性继承
     */
    propertyExtends(data){
        var pro = {
            appid : this.appid,
            mch_id : this.mch_id,
            device_info : data.device_info
        }
        Object.assign(pro, data);
        return pro;
    }

    /**
     *构建一个微信的下单对象
     */
    buildWeiChatPlaceOrder(data){
        let placeOrder = this.propertyExtends(data),
            combineStr = WeixinUtil.buildSortString(placeOrder) + "&key=" + this.appKey;
        placeOrder.sign = this.generateSign(combineStr);
        return placeOrder;
    }

    generateSign(str){
        str = unescape(encodeURIComponent(str));
        var sign = crypto.createHash("md5").update(str).digest("hex").toUpperCase();
        return sign;
    }
}

Weixin.placeOrderUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder";
Weixin.codeSuccess = 200;

/**
 *微信公共类
 */
class WeixinUtil{

    /**
     * 将json转化为xml
     */
    static convertObjToXml(josnObj){
        var xml = xmlbuilder.create("xml");
        for(var key in josnObj) {
            xml.ele(key, null, josnObj[key]);
        }
        xml.end({pretty: true});
        return xml;
    }

    /**
     * 返回与微信交互时的 https 参数
     */
    static getWeichatInteractiveHttpOptions(urlStr, method){
        if (!urlStr) {
            throw new Error("getWeichatInteractiveHttpOptions -> need url");
        }
        method = method || "POST";
        let parseUrl = url.parse(urlStr);
        return {
            hostname: parseUrl.hostname,
            path: parseUrl.path,
            protocol: parseUrl.protocol,
            method: method,
            headers: {
                "Content-Type" : "application/xml"
            }
        };
    }

    static parseXml(data){
        return new Promise(function(reslove, reject){
            parseXml(data, function(err, obj){
                if(err) return reject(err);
                return reslove(obj);
            })
        })
    }

    static  formatResponseData(response){
        var result = {};
        for(var key in response) {
            result[key] = response[key][0];
        }
        return result;
    }

    static buildSortString(obj, exclude){
        let keys = Object.keys(obj).sort(),
            str = "";
        exclude = Util.isArray(exclude) ? exclude : [];
        keys = Util.differenceSet(keys, exclude);
        for(var i = 0; i < keys.length; i++) {
            var value = (obj[keys[i]] || "").toString();
            if (!!value) {
                str += keys[i] + "=" + value + "&";
            }
        }
        // 去掉结尾的 &
        str = str.slice(0, str.length -1);
        return str;
    }
}

module.exports = Weixin;